
#include "ut.h"

#include <ql/alloc.h>

/*
 * test fixed allocator that is not capable to free elements allocated
 */

#define QL_FIXED_ALLOC_NS(n)            int_##n
#define QL_FIXED_ALLOC_ELEMENT_TYPE     int
#define QL_FIXED_ALLOC_XMALLOC          ql_malloc
#define QL_FIXED_ALLOC_XFREE            ql_free
#define QL_FIXED_ALLOC_INITIAL_CHUNK_SIZE (4)
#define QL_FIXED_ALLOC_GET_ALLOCATOR_STATUS_REQUIRED
#define QL_FIXED_ALLOC_FOREACH_REQUIRED

#include <qltemplates/fixed_alloc.h>

#define QL_NS(name) int_##name
#define QL_ELEMENT_TYPE int
#include "test_alloc_foreach.h"


static void fxtst1()
{
    int_Allocator allocator;
    int * num;
    int * num1;
    int * num2;
    size_t used, allocated;

    QL_UT_BEGIN("allocator w/o free function");

    int_init_allocator(&allocator);

    int_get_allocator_status(&allocator, &used, &allocated);
    QL_UT_VERIFY((used == 0) && (allocated == 0));

    QL_UT_VERIFY(num = int_alloc_elem(&allocator));
    *num = -1;
    int_get_allocator_status(&allocator, &used, &allocated);
    QL_UT_VERIFY((used == 1) && (allocated == 4)); /* 4 is initial chunk size */

    num1 = int_alloc_elem(&allocator);
    *num1 = 0xFEEDFEED;
    int_get_allocator_status(&allocator, &used, &allocated);
    QL_UT_VERIFY((used == 2) && (allocated == 4));

    QL_UT_VERIFY(num != num1);
    QL_UT_VERIFY(num2 = int_alloc_elem(&allocator));
    *num2 = 0xAEF0AEF1;
    QL_UT_VERIFY((num2 != num1) && (num != num2));

    int_get_allocator_status(&allocator, &used, &allocated);
    QL_UT_VERIFY((used == 3) && (allocated == 4));

    num = int_alloc_elem(&allocator);
    int_get_allocator_status(&allocator, &used, &allocated);
    *num = -4;
    QL_UT_VERIFY((used == 4) && (allocated == 4));

    num = int_alloc_elem(&allocator);
    *num = 295;
    int_get_allocator_status(&allocator, &used, &allocated);
    QL_UT_VERIFY((used == 5) && (allocated == 8));

    {
        int arr[] = { -1, 0xFEEDFEED, 0xAEF0AEF1, -4, 295 };
        int_test_foreach("foreach test for non-free allocator", &allocator, arr, sizeof(arr)/sizeof(arr[0]));
    }

    int_uninit_allocator(&allocator);
    QL_UT_END();
}

/*
 * test fixed allocator that is capable to free underlying elements
 */

#define QL_FIXED_ALLOC_NS(n)            dbl_##n
#define QL_FIXED_ALLOC_ELEMENT_TYPE     double
#define QL_FIXED_ALLOC_XMALLOC          ql_malloc
#define QL_FIXED_ALLOC_XFREE            ql_free
#define QL_FIXED_ALLOC_INITIAL_CHUNK_SIZE (3)
#define QL_FIXED_ALLOC_FREE_FUNCTION_REQUIRED
#define QL_FIXED_ALLOC_GET_ALLOCATOR_STATUS_REQUIRED
#define QL_FIXED_ALLOC_FOREACH_REQUIRED

#include <qltemplates/fixed_alloc.h>

#define QL_NS(name) dbl_##name
#define QL_ELEMENT_TYPE double
#include "test_alloc_foreach.h"

static double get_next_num(size_t index)
{
    return (double)((index & 1) ? -(double)index : index + 1000.0);
}

static void fxtst2()
{
    dbl_Allocator allocator;
    double * num1;
    double * num2;
    double * num3;
    double a;
    size_t alloc1;
    size_t next_num = 0;
    size_t used, allocated;
    QL_UT_BEGIN("allocator w/free function");

    dbl_init_allocator(&allocator);

    dbl_get_allocator_status(&allocator, &used, &allocated);
    QL_UT_VERIFY((used == 0) && (allocated == 0));

    num1 = dbl_alloc_elem(&allocator);
    *num1 = 9.9e10;

    dbl_get_allocator_status(&allocator, &used, &allocated);
    alloc1 = allocated;
    QL_UT_VERIFY((used == 1) && ((allocated % 4) == 0));

    dbl_free_elem(&allocator, num1);
    dbl_get_allocator_status(&allocator, &used, &allocated);
    QL_UT_VERIFY((used == 0) && (allocated == alloc1));

    num2 = dbl_alloc_elem(&allocator);
    a = get_next_num(next_num++);
    *num2 = a;
    QL_UT_VERIFY(num1 == num2);

    dbl_get_allocator_status(&allocator, &used, &allocated);
    QL_UT_VERIFY((used == 1) && (allocated == alloc1));

    num3 = dbl_alloc_elem(&allocator);
    *num3 = get_next_num(next_num++);
    QL_UT_VERIFY((num3 != num2) && (*num2 == a));

    num1 = dbl_alloc_elem(&allocator);
    a = 2.3e14;
    *num1 = a;
    QL_UT_VERIFY((*num3 != *num1) && (*num1 == a));

    dbl_get_allocator_status(&allocator, &used, &allocated);
    QL_UT_VERIFY((used == 3) && (allocated == alloc1));

    

    // allocate 3 * alloc1 elements - this must multiply elements
    {
        size_t i;
        const size_t alloc_series = 3;
        size_t cnt = alloc1 * alloc_series;
        size_t expected_used_size = cnt + used;
        size_t expected_alloc_size = cnt + alloc1;

        a = *num3;

        for (i = 0; i < cnt; ++i)
        {
            double b;
            num2 = dbl_alloc_elem(&allocator);
            b = get_next_num(next_num++);
            *num2 = b;
            QL_UT_VERIFY_SILENT((num3 != num2) && (*num2 == b) && (*num3 == a));

            a = b;
            num3 = num2;
        }

        dbl_get_allocator_status(&allocator, &used, &allocated);
        QL_UT_VERIFY((used == expected_used_size) && (allocated == expected_alloc_size));

        dbl_free_elem(&allocator, num1);
        dbl_get_allocator_status(&allocator, &used, &allocated);
        QL_UT_VERIFY((used == (expected_used_size - 1)) && (allocated == expected_alloc_size));

        num2 = dbl_alloc_elem(&allocator);
        *num2 = get_next_num(next_num++);
        dbl_get_allocator_status(&allocator, &used, &allocated);
        QL_UT_VERIFY((used == expected_used_size) && (used == next_num) && (allocated == expected_alloc_size));
    }

    {
        double * arr = ql_malloc(sizeof(double)*next_num);
        size_t i;
        for (i = 0; i < next_num; ++i)
        {
            arr[i] = get_next_num(i);
        }

        dbl_test_foreach("foreach test for free allocator", &allocator, arr, next_num);

        ql_free(arr);
    }

#if 0
    // this must produce an assertion that tells that element released twice
    dbl_free_elem(&allocator, num1);
#endif

    dbl_uninit_allocator(&allocator);
    QL_UT_END();
}

/*
 * function that launches tests
 */
void test_fixed_alloc()
{
    fxtst1();
    fxtst2();
}

